﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Windows.Input;
using Peter.Common;
using Peter.Common.Dialog;
using Peter.Common.MainMenu;
using Peter.Sdw.Data;
using Peter.Sdw.Properties;

namespace Peter.Sdw.Logic
{
   /// <summary>
   /// Represents a book.
   /// </summary>
   public sealed class BookModel : PeterDocument
   {
      private int m_TermId;
      private string m_Title;
      private string m_Summary;
      private string m_TermName;
      private string m_SubTitle;
      private bool m_IsLoading;
      private RelayCommand m_CmdSelectTerm;
      private RelayCommand m_CmdAddVersion;
      private VersionModel m_SelectedVersion;
      private const string NEW_BOOK = "New Book";
      private readonly ObservableCollection<VersionModel> m_Versions;

      /// <summary>
      /// Initializes a new book model.
      /// </summary>
      public BookModel ()
      {
         this.Title = NEW_BOOK;
         this.Icon = MainMenuIcon.Book;
         this.m_Versions = new ObservableCollection<VersionModel> ();
      }

      #region Book Data

      /// <summary>
      /// Gets or Sets the id of the book.
      /// </summary>
      public int Id { get; set; }

      /// <summary>
      /// Gets or Sets if it is ok to send any updates to the server.
      /// </summary>
      public bool OkToUpdate { get; set; }

      /// <summary>
      /// Gets or Sets the title of the book.
      /// </summary>
      public new string Title
      {
         get { return this.m_Title; }
         set
         {
            if (this.m_Title != value) {
               this.m_Title = value;
               this.RaisePropertyChanged ("Title");
               this.Edit ();
            }
         }
      }

      /// <summary>
      /// Gets or Set the sub title of the book.
      /// </summary>
      public string SubTitle
      {
         get { return this.m_SubTitle; }
         set
         {
            if (this.m_SubTitle != value) {
               this.m_SubTitle = value;
               this.RaisePropertyChanged ("SubTitle");
               this.Edit ();
            }
         }
      }

      /// <summary>
      /// Gets or Sets a brief summary of the book.
      /// </summary>
      public string Summary
      {
         get { return this.m_Summary; }
         set
         {
            if (this.m_Summary != value) {
               this.m_Summary = value;
               this.RaisePropertyChanged ("Summary");
               this.Edit ();
            }
         }
      }

      /// <summary>
      /// Gets or Sets the associated term's id.
      /// </summary>
      public int TermId
      {
         get { return this.m_TermId; }
         set
         {
            if (this.m_TermId != value) {
               this.m_TermId = value;
               this.RaisePropertyChanged ("TermId");
               this.Edit ();
            }
         }
      }

      /// <summary>
      /// Gets or Sets the default version.
      /// </summary>
      public int DefaultVersionId { get; set; }

      /// <summary>
      /// Gets or Sets the term name.
      /// </summary>
      public string TermName
      {
         get { return this.m_TermName; }
         set
         {
            if (this.m_TermName != value) {
               this.m_TermName = value;
               this.RaisePropertyChanged ("TermName");
            }
         }
      }

      #endregion

      /// <summary>
      /// Gets the list of versions.
      /// </summary>
      public ObservableCollection<VersionModel> Versions { get { return this.m_Versions; } }

      /// <summary>
      /// Gets or Sets the selected version.
      /// </summary>
      public VersionModel SelectedVersion
      {
         get { return this.m_SelectedVersion; }
         set
         {
            this.m_SelectedVersion = value;
            if (this.m_SelectedVersion != null)
               this.m_SelectedVersion.GetSubBooks ();
            this.RaisePropertyChanged ("SelectedVersion");
         }
      }

      /// <summary>
      /// Gets or Sets if we are loading data.
      /// </summary>
      public bool IsLoading
      {
         get { return this.m_IsLoading; }
         set
         {
            this.m_IsLoading = value;
            this.RaisePropertyChanged ("IsLoading");
         }
      }

      /// <summary>
      /// Gets the list of version from the server.
      /// </summary>
      public void GetVersions ()
      {
         this.IsLoading = true;
         this.Versions.Clear ();
         WebQueue.Instance.WebGetComplete += this.OnGetCompleted;
         WebQueue.Instance.Get (string.Format (Constants.URL_VERSION_LIST, this.Id));
      }

      /// <summary>
      /// Posts edit data to the server.
      /// </summary>
      private void Edit ()
      {
         if (!this.OkToUpdate || string.IsNullOrEmpty (this.Title) || this.Title == NEW_BOOK ||
             string.IsNullOrEmpty (this.Summary) || this.TermId == 0) return;
         var parameters = new NameValueCollection
         {
            {Constants.REQUEST_TOKEN, WebQueue.Instance.RequestToken},
            {"id", this.Id.ToString (CultureInfo.InvariantCulture)},
            {"title", this.Title},
            {"subTitle", this.SubTitle},
            {"summary", this.Summary},
            {"termId", this.TermId.ToString (CultureInfo.InvariantCulture)}
         };
         WebQueue.Instance.WebPostComplete += this.OnPostCompleted;
         WebQueue.Instance.Post (this.Id == 0 ? Constants.URL_CREATE_BOOK : Constants.URL_EDIT_BOOK, parameters);
      }

      /// <summary>
      /// Gets the command to show the new version dialog.
      /// </summary>
      public ICommand AddVersionCommand
      {
         get { return this.m_CmdAddVersion ?? (this.m_CmdAddVersion = new RelayCommand (this.OnAddVersion, this.CanAddVersion)); }
      }

      /// <summary>
      /// Gets the select term command.
      /// </summary>
      public ICommand SelectTermCommand
      {
         get { return this.m_CmdSelectTerm ?? (this.m_CmdSelectTerm = new RelayCommand (this.OnSelectTerm)); }
      }

      /// <summary>
      /// Shows the select term dialog.
      /// </summary>
      /// <param name="obj">Command Parameter</param>
      private void OnSelectTerm (object obj)
      {
         if (this.Peter.ShowDialog (SdwMainModel.TermSelector) != DialogCloseAction.Ok) return;
         this.TermName = SdwMainModel.TermSelector.TermList.SelectedTerm.Name;
         this.TermId = SdwMainModel.TermSelector.TermList.SelectedTerm.Id;
      }

      /// <summary>
      /// Checks to see if the add version command can execute.
      /// </summary>
      /// <param name="obj">Command parameter</param>
      /// <returns>True if ok to execute, otherwise false.</returns>
      private bool CanAddVersion (object obj)
      {
         return this.Peter != null && this.Id > 0;
      }

      /// <summary>
      /// Occurs when the user selects to add a new version.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void OnAddVersion (object obj)
      {
         var model = new VersionModel (this);
         this.Versions.Add (model);
         this.SelectedVersion = model;
      }

      /// <summary>
      /// Occurs when a web request is completed.
      /// </summary>
      /// <param name="sender">WebQueue.</param>
      /// <param name="e">WebRequestEventArgs</param>
      private void OnGetCompleted (object sender, WebRequestEventArgs e)
      {
         try {
            if (e.Url == string.Format (Constants.URL_VERSION_LIST, this.Id)) {
               WebQueue.Instance.WebGetComplete -= this.OnGetCompleted;
               if (e.Response.status == Constants.SUCCESS) {
                  foreach (var version in e.Response.versions) {
                     this.Versions.Add (new VersionModel (this) {
                           Id = version.id,
                           Title = version.title,
                           DefaultReadChapter = version.defaultread,
                           Contents = version.contents,
                           PublishDate = version.publishdate,
                           SourceName = version.sourcename,
                           SourceUrl = version.sourceurl,
                           TermId = version.termid,
                           TermName = version.termname
                        });
                  }
                  foreach (var version in this.Versions) {
                     version.OkToUpdate = true;
                  }
                  this.SelectedVersion = this.Versions.FirstOrDefault ();
               }
               this.IsLoading = false;
            }
         } catch (Exception ex) {
            Console.Error.WriteLine (ex.Message);
         }
      }

      /// <summary>
      /// Occurs when a web post request is completed.
      /// </summary>
      /// <param name="sender">WebQueue.</param>
      /// <param name="e">WebRequestEventArgs</param>
      private void OnPostCompleted (object sender, WebRequestEventArgs e)
      {
         try {
            if (e.Url == Constants.URL_CREATE_BOOK) {
               WebQueue.Instance.WebPostComplete -= this.OnPostCompleted;
               if (e.Response.status == Constants.SUCCESS) {
                  Console.WriteLine (Resources.Create_successful);
                  this.Id = e.Response.id;
               }
            } else if (e.Url == Constants.URL_EDIT_BOOK) {
               WebQueue.Instance.WebPostComplete -= this.OnPostCompleted;
               if (e.Response.status == Constants.SUCCESS)
                  Console.WriteLine (Resources.Edit_successful);
            }
         } catch (Exception ex) {
            Console.WriteLine (ex.Message);
         }
      }
   }
}
